#pragma once

#include <iostream>
#include <string>
#include <signal.h>

#include <jsoncpp/json/json.h>
#include "../comm/log.hpp"
#include "../comm/util.hpp"
#include "compiler.hpp"
#include "runner.hpp"

namespace ns_compile_and_run
{
    class Compile_And_Run
    {
    public:
        /***************************************
         * 输入:
         * code： 用户提交的代码
         * input: 用户给自己提交的代码对应的输入，不做处理
         * cpu_limit: 时间要求
         * mem_limit: 空间要求
         *
         * 输出:
         * 必填
         * status: 状态码
         * reason: 请求结果
         * 选填：
         * stdout: 我的程序运行完的结果
         * stderr: 我的程序运行完的错误结果
         *
         * 参数：
         * in_json: {"code": "#include...", "input": "","cpu_limit":1, "mem_limit":10240}
         * out_json: {"status":"0", "reason":"","stdout":"","stderr":"",}
         * ************************************/
        static void Start(const std::string &in_json, std::string &out_json)
        {
            Json::Value in_value;
            Json::Reader reader;
            reader.parse(in_json, in_value);

            std::string code = in_value["code"].asString();
            int cpu_limit = in_value["cpu_limit"].asInt();
            int mem_limit = in_value["mem_limit"].asInt();

            int status_code = 0;       // 程序运行返回码
            std::string file_name;     // 唯一文件名，无路径和后缀
            int run_code = 0;          // 临时程序运行返回码
            Json::Value out_value;     // 输出json
            Json::StyledWriter writer; // 序列化

            if (code.size() == 0)
            {
                // 提交代码为空白
                status_code = -1;
                goto END;
            }

            file_name = ns_util::FileUtil::UniqueFileName();                            // 生成唯一文件名，无路径和后缀
            if (!ns_util::FileUtil::WriteFile(ns_util::PathUtil::Src(file_name), code)) // 将客户端发来的代码写入临时源文件
            {
                // 写文件错误
                status_code = -2;
                goto END;
            }
            if (!ns_compiler::Compiler::Compile(file_name)) // 编译
            {
                // 编译失败
                status_code = -3;
                goto END;
            }

            run_code = ns_runner::Runner::Run(file_name, cpu_limit, mem_limit); // 运行
            if (run_code < 0)
            {
                // 程序运行时内部错误
                status_code = -2;
                goto END;
            }
            else
            {
                // 程序正确运行返回0，报错返回信号编号
                status_code = run_code;
                goto END;
            }

        END:
            out_value["status"] = status_code;                        // 返回值
            out_value["reason"] = CodeToDesc(status_code, file_name); // 原因
            if (status_code == 0)
            {
                // 编译并且运行成功
                std::string _stdout, _stderr;
                if(ns_util::FileUtil::ReadFile(ns_util::PathUtil::RunOut(file_name), _stdout, true) && 
                        ns_util::FileUtil::ReadFile(ns_util::PathUtil::RunErr(file_name), _stderr, true))
                {
                    // 读取文件失败
                    // TODO
                }
                out_value["stdout"] = _stdout; // 程序运行输出
                out_value["stderr"] = _stderr; // 程序运行报错
            }
            out_json = writer.write(out_value); // 序列化
            RemoveTmpFile(file_name);
        }

    private:
        // 返回码对应描述
        static std::string CodeToDesc(int code, const std::string &file_name)
        {
            std::string desc;
            switch (code)
            {
            case -1:
                desc = "提交代码为空。";
                break;
            case -2:
                desc = "内部错误。";
                break;
            case -3:
                ns_util::FileUtil::ReadFile(ns_util::PathUtil::CompileErr(file_name), desc, true);
                break;
            case 0:
                desc = "编译运行成功。";
                break;
            case SIGABRT:
                desc = "内存不足。";
                break;
            case SIGSEGV:
                desc = "内存不足。";
                break;
            case SIGXCPU:
                desc = "CPU运行超时。";
                break;
            default:
                desc = "Unknow code: " + std::to_string(code);
                break;
            }

            return desc;
        }

        // 删除临时文件
        static void RemoveTmpFile(const std::string& file_name)
        {
            std::string src_file = ns_util::PathUtil::Src(file_name);
            if(ns_util::FileUtil::IsExists(src_file)) unlink(src_file.c_str());

            std::string exe_file = ns_util::PathUtil::Exe(file_name);
            if(ns_util::FileUtil::IsExists(exe_file)) unlink(exe_file.c_str());

            std::string compile_err_file = ns_util::PathUtil::CompileErr(file_name);
            if(ns_util::FileUtil::IsExists(compile_err_file)) unlink(compile_err_file.c_str());

            std::string stdin_file = ns_util::PathUtil::RunIn(file_name);
            if(ns_util::FileUtil::IsExists(stdin_file)) unlink(stdin_file.c_str());

            std::string stdout_file = ns_util::PathUtil::RunOut(file_name);
            if(ns_util::FileUtil::IsExists(stdout_file)) unlink(stdout_file.c_str());

            std::string stderr_file = ns_util::PathUtil::RunErr(file_name);
            if(ns_util::FileUtil::IsExists(stderr_file)) unlink(stderr_file.c_str());
        }
    };
}